package com.newland.starpos.base;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.hibernate.transform.Transformers;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.HibernateTemplate;
import org.springframework.orm.hibernate3.SessionHolder;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.util.Assert;

/**
 * GenericDAO接口的Hibernate实现。
 */
public abstract class HibernateGenericDao<T, ID extends Serializable> implements GenericDao<T, ID> {

	protected static Logger logger = LoggerFactory.getLogger(HibernateGenericDao.class);

	@Autowired
	protected HibernateTemplate hibernateTemplate;
	/** 持久化对象的实际类型 。 */
	private Class<T> entityClass;

	/**
	 * 如需运行时变更持久化类型，可override本函数。
	 * 
	 * @return 持久化对象类型
	 */
	public Class<T> getEntityClass() {
		return entityClass;
	}

	/**
	 * 构造函数(默认)
	 */
	public HibernateGenericDao() {
		// 获取当前Class为泛型超类的第一个类型参数
		this.entityClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass())
				.getActualTypeArguments()[0];

	}

	public void flush() {
		hibernateTemplate.flush();
	}

	public T findById(ID id) {
		return (T) hibernateTemplate.get(entityClass, id);
	}

	public T findById(ID id, boolean lock) {
		if (lock) {
			return (T) hibernateTemplate.get(entityClass, id, LockMode.UPGRADE);
		} else {
			return findById(id);
		}
	}

	@SuppressWarnings("unchecked")
	public List<T> findAll() {
		DetachedCriteria dc = DetachedCriteria.forClass(getEntityClass());
		return (List<T>) hibernateTemplate.findByCriteria(dc);
	}

	public void delete(T entity) {
		Assert.notNull(entity);
		hibernateTemplate.delete(entity);
	}

	public void deleteById(ID id) {
		Assert.notNull(id);
		T entity = this.findById(id);
		if (entity != null)
			this.delete(entity);
	}

	public T save(T entity) {
		Assert.notNull(entity);
		hibernateTemplate.save(entity);
		return entity;
	}

	public T update(T entity) {
		Assert.notNull(entity);
		hibernateTemplate.update(entity);
		return entity;
	}

	public T saveOrUpdate(T entity) {
		Assert.notNull(entity);
		hibernateTemplate.saveOrUpdate(entity);
		return entity;
	}

	/**
	 * 执行HQL语句，限用于批量修改/删除操作。
	 * 
	 * @param hql
	 * @param values
	 * @return 返回删除/更新记录数
	 */
	protected int excuteByHql(final String hql, final Object values) {
		Assert.hasText(hql);
		return hibernateTemplate.executeWithNativeSession(new HibernateCallback<Integer>() {
			public Integer doInHibernate(Session session) throws HibernateException {

				Query query = createQueryByHql(session, hql, values);
				return query.executeUpdate();
			}
		});

	}

	/**
	 * 执行SQL语句，限用于批量修改/删除操作。
	 * 
	 * @param sql
	 * @param values
	 * @return 返回删除/更新记录数
	 */
	public int excuteBySql(final String sql, final Object values) {
		Assert.hasText(sql);
		return hibernateTemplate.executeWithNativeSession(new HibernateCallback<Integer>() {
			public Integer doInHibernate(Session session) throws HibernateException {

				SQLQuery query = createQueryBySql(session, sql, values);
				return query.executeUpdate();
			}
		});

	}

	protected void applyTransactionTimeout(Query query) {
		if (null != hibernateTemplate.getSessionFactory()) {
			SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager
					.getResource(hibernateTemplate.getSessionFactory());
			if (null != sessionHolder && sessionHolder.hasTimeout()) {
				query.setTimeout(sessionHolder.getTimeToLiveInSeconds());
			}
		}
	}

	/**
	 *
	 * @param session
	 * @param hql
	 * @param param
	 *            可以是 Collection 或者Map<String,Object>
	 * @return
	 */
	protected Query createQueryByHql(Session session, String hql, Object param) {
		Assert.hasText(hql);
		Query query = session.createQuery(hql);
		applyTransactionTimeout(query);
		setQueryParam(query, param);
		return query;
	}

	/**
	 *
	 * @param session
	 * @param sql
	 * @param param
	 *            可以是 Collection 或者Map<String,Object>
	 * @return
	 */
	protected SQLQuery createQueryBySql(Session session, String sql, Object param) {
		Assert.hasText(sql);
		SQLQuery query = session.createSQLQuery(sql);
		applyTransactionTimeout(query);
		setQueryParam(query, param);
		return query;
	}

	private void setQueryParam(Query query, Object param) {
		if (param != null) {
			if (param instanceof Collection) {
				Collection values = (Collection) param;
				int i = 0;
				for (Iterator iterator = values.iterator(); iterator.hasNext();) {
					query.setParameter(i++, iterator.next());
				}
			} else if (param instanceof Map) {
				Map<String, Object> paramMap = (HashMap<String, Object>) param;
				for (Map.Entry<String, Object> entry : paramMap.entrySet()) {
					if (entry.getValue() instanceof Collection)
						query.setParameterList(entry.getKey(), (Collection<?>) entry.getValue());
					else if (entry.getValue() instanceof Object[])
						query.setParameterList(entry.getKey(), (Object[]) entry.getValue());
					else
						query.setParameter(entry.getKey(), entry.getValue());
				}
			}
		}
	}

	/**
	 * HQL单一查询，例如select count(*)/sum(property) from ... 封装 findUniqueByHql的两个重载
	 * ，统一入口
	 * 
	 * @param hql
	 * @param param
	 *            可以是 Collection 或者Map<String,Object>
	 * @return
	 */
	protected Object findUniqueByHql(final String hql, final Object param) {
		return hibernateTemplate.executeWithNativeSession(new HibernateCallback<Object>() {
			public Object doInHibernate(Session session) throws HibernateException {
				return createQueryByHql(session, hql, param).uniqueResult();
			}
		});
	}

	/**
	 * SQL单一查询，例如select count(*)/sum(property) from ...
	 * 封装findUniqueBySql的两个重载方法，统一入口
	 * 
	 * @param sql
	 * @param param
	 *            可以是 Collection 或者Map<String,Object>
	 * @return
	 */
	protected Object findUniqueBySql(final String sql, final Object param) {
		return hibernateTemplate.executeWithNativeSession(new HibernateCallback<Object>() {
			@Override
			public Object doInHibernate(Session session) throws HibernateException {
				return createQueryBySql(session, sql, param).uniqueResult();
			}
		});
	}

	/**
	 * 执行count查询获得本次Hql查询所能获得的对象总数<br>
	 * 只能处理简单的hql语句，不支持union等复杂的hql查询。<br>
	 * 复杂的查询语句请通过建立视图或重构数据库设计解决。
	 * 
	 * @param values
	 *            可以是 Collection 或者Map<String,Object>
	 */
	protected int countResultByHql(final String hql, final Object values) {
		// 剔除语句中select部分和order by部分
		String s = hql.toUpperCase();
		int i = s.indexOf("FROM ");
		if (i > -1) {
			i = i + 5;
		} else {
			i = 0;
		}
		int j = s.indexOf(" ORDER BY");
		if (j == -1) {
			j = s.length();
		}
		s = hql.substring(i, j);
		// 拼装成select count(*)语句
		String countHql = "SELECT COUNT(*) FROM " + s;
		Long count = (Long) findUniqueByHql(countHql, values);
		return count == null ? 0 : count.intValue();
	}

	/**
	 * count查询获得本次sql查询所能获得的对象总数<br>
	 * 
	 * @param sql
	 * @param values
	 *            可以是 Collection 或者Map<String,Object>
	 * @return
	 */
	protected int countResultBySql(final String sql, final Object values) {
		// 剔除语句中select部分和order by部分
		String s = sql.toUpperCase();
		int i = s.indexOf("FROM ");
		if (i > -1) {
			i = i + 5;
		} else {
			i = 0;
		}
		int j = s.indexOf(" ORDER BY");
		if (j == -1) {
			j = s.length();
		}
		s = sql.substring(i, j);
		// 拼装成select count(*)语句
		String countSql = "SELECT COUNT(*) FROM " + s;
		int count = Integer.parseInt(findUniqueBySql(countSql, values).toString());
		return count == 0 ? 0 : count;
	}

	/**
	 * HQL查询，支持变参列表<br>
	 */
	@SuppressWarnings("unchecked")
	protected List<T> findListByHql(final String hql, final Object values) {
		return (List<T>) hibernateTemplate.executeWithNativeSession(new HibernateCallback<List<T>>() {
			@Override
			public List<T> doInHibernate(Session session) throws HibernateException {
				Query query = createQueryByHql(session, hql, values);
				if (query != null)
					return query.list();
				return null;
			}
		});
	}

	/**
	 * @param values
	 *            可以是 Collection 或者Map<String,Object> SQL查询 ，支持变参列表
	 */
	@SuppressWarnings("unchecked")
	protected List<T> findListBySql(final String sql, final Object values) {
		return (List<T>) hibernateTemplate.executeWithNativeSession(new HibernateCallback<List<T>>() {
			public List<T> doInHibernate(Session session) throws HibernateException {
				SQLQuery query = createQueryBySql(session, sql, values).addEntity(entityClass);
				if (query != null)
					return query.list();
				return null;
			}
		});
	}

	/**
	 * 获取所有记录，分页返回
	 * 
	 * @return 分页查询结果
	 */
	@SuppressWarnings("unchecked")
	protected Page<T> findAll(Pageable pageable) {
		DetachedCriteria dc = DetachedCriteria.forClass(getEntityClass());
		// 查总记录数
		dc.setProjection(Projections.rowCount());
		int totalCount = ((Integer) hibernateTemplate.findByCriteria(dc).get(0)).intValue();
		dc.setProjection(null);
		List<T> list = (List<T>) hibernateTemplate.findByCriteria(dc, pageable.getStartIndex(), pageable.getPageSize());

		return new Page<T>(list, totalCount, pageable);
	}

	/**
	 * HQL分页查询
	 */
	protected Page<T> findPageByHql(final String hql, final Pageable pageable, final Object values) {
		return hibernateTemplate.executeWithNativeSession(new HibernateCallback<Page<T>>() {
			@SuppressWarnings("unchecked")
			public Page<T> doInHibernate(Session session) throws HibernateException {
				// 查总记录数
				int total = countResultByHql(hql, values);
				Query query = createQueryByHql(session, hql, values);
				query.setFirstResult(pageable.getStartIndex());
				query.setMaxResults(pageable.getPageSize());

				return new Page<T>(query.list(), total, pageable);
			}
		});

	}

	/**
	 * 原生sql分页查询
	 * 
	 * @param sql
	 * @param pageable
	 * @param values
	 * @return
	 */
	protected Page<T> findPageBySql(final String sql, final Pageable pageable, final Object values) {
		return (Page<T>) hibernateTemplate.executeWithNativeSession(new HibernateCallback<Page<T>>() {
			@SuppressWarnings("unchecked")
			public Page<T> doInHibernate(Session session) throws HibernateException {
				// 查总记录数((BigDecimal)query.uniqueResult()).intValue();
				int totalCount = countResultBySql(sql, values);
				// 查分页数据，先创建分页对象是为了对startIndex和pageSize进行容错处理
				SQLQuery query = createQueryBySql(session, sql, values).addEntity(entityClass);
				query.setFirstResult(pageable.getStartIndex());
				query.setMaxResults(pageable.getPageSize());
				return new Page<T>(query.list(), totalCount, pageable);

			}
		});

	}

	/**
	 * 根据属性值查询对象
	 * 
	 * @param propName
	 *            属性名
	 * @param propValue
	 *            属性值
	 */
	protected List<T> findByProperty(String propName, Object propValue) {
		Assert.hasText(propName);
		DetachedCriteria dc = DetachedCriteria.forClass(getEntityClass());
		dc.add(Restrictions.eq(propName, propValue));
		return (List<T>) hibernateTemplate.findByCriteria(dc);
	}

	@SuppressWarnings("unchecked")
	protected List<T> findByProperty(String[] propNames, Object[] propvalues) {
		Assert.notNull(propvalues);
		Assert.notNull(propNames);
		int i = 0;
		DetachedCriteria dc = DetachedCriteria.forClass(getEntityClass());
		for (String propName : propNames) {
			if (propName != null) {
				if (propvalues.length > i)
					dc.add(Restrictions.eq(propName, propvalues[i]));
				else {
					logger.warn("propNames are more than propValues' size!");
					break;
				}
			}
			i++;
		}
		return (List<T>) this.hibernateTemplate.findByCriteria(dc);
	}

	/**
	 * SQL查询 ，支持变参列表
	 */
	protected List findBySqlTransMap(final String sql, final Class clazz, final Object values) {
		return (List) hibernateTemplate.executeWithNativeSession(new HibernateCallback<List>() {
			public List doInHibernate(Session session) throws HibernateException {
				SQLQuery query = createQueryBySql(session, sql, values);
				if (clazz.getName().equals("java.util.HashMap")) {
					query.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
				} else {
					query.setResultTransformer(Transformers.aliasToBean(clazz));
				}
				return query.list();
			}
		});
	}

	protected Page findBySqlTransMap(final String sql, final Pageable pageable, final Class clazz,
			final Object values) {
		return (Page) hibernateTemplate.executeWithNativeSession(new HibernateCallback() {
			public Object doInHibernate(Session session) throws HibernateException {
				// 查总记录数((BigDecimal)query.uniqueResult()).intValue();
				int totalCount = countResultBySql(sql, values);

				// 查分页数据，先创建分页对象是为了对startIndex和pageSize进行容错处理
				SQLQuery query = createQueryBySql(session, sql, values);
				if (clazz.getName().equals("java.util.HashMap")) {
					query.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
				} else {
					query.setResultTransformer(Transformers.aliasToBean(clazz));
				}
				query.setFirstResult(pageable.getStartIndex());
				query.setMaxResults(pageable.getPageSize());
				return new Page<T>(query.list(), totalCount, pageable);
			}
		});

	}

}
